<?php
/**
 * Created by IntelliJ IDEA.
 * User: ilya
 * Date: 23.03.11
 * Time: 20:27
 */

class ResultsTable {

    private $user_id;
    private $user_data_keys;
    private $is_admin;
    private $contest_id;

    private $problem_pos; //problem id -> contest pos
    private $problem_keys = array(); //problem id -> array of keys

    private $user_login; //user id -> user login
    private $user_data; //user id -> user data

    private $table = array(); //user id -> problem id -> values array

    private function key_to_ind($key, $problem_id) {
        if (!isset($this->problem_keys[$problem_id]))
            $this->problem_keys[$problem_id] = array();

        $keys = &$this->problem_keys[$problem_id];

        $key_pos = array_search($key, $keys);
        if ($key_pos === false)
            $keys[] = $key;

        return array_search($key, $keys);
    }

    public function __construct($contest_id, $users_data, $user_id, $is_admin) {
        $this->user_id = $user_id;
        $this->is_admin = $is_admin;
        $this->contest_id = $contest_id;

        $this->fill_user_data_keys($users_data);

        $this->consider_problems($contest_id);

        $this->consider_users($contest_id, $user_id, $is_admin);

        $this->fillTable();
    }

    /**
     * @return GetContestResultsResponse
     */
    public function getTable() {
        $result = new GetContestResultsResponse();

        $result->headers = array();
        $result->minorHeaders = array();

        if ($this->is_admin) {
            $result->headers[] = 'admin info';
            $result->minorHeaders[] = array('id', 'login');

            $before_problems_headers = 2;
            $participant_col = 1;
        } else {
            $before_problems_headers = 1;
            $participant_col = 0;
        }

        $result->headers[] = 'participant';
        $result->minorHeaders[] = $this->user_data_keys;

        //headers for problems
        foreach ($this->problem_pos as $pid => $pos) {
            $result->headers[$pos + $before_problems_headers] = strval($pos + 1);
            $result->minorHeaders[$pos + $before_problems_headers] = $this->problem_keys[$pid];
        }

        $result->userLine = -1;

        $result->table = array();
        foreach ($this->user_login as $uid => $login) {
            $ind = count($result->table);
            if ($uid == $this->user_id)
                $result->userLine = $ind;

            //add new line
            $result->table[] = array();

            $result->table[$ind][$participant_col] = array();
            foreach ($this->user_data_keys as $key_ind=>$key) {
                //TODO debug @
                @$result->table[$ind][$participant_col][$key_ind] = $this->user_data[$uid][$key_ind];
            }

            if ($this->is_admin) {
                $result->table[$ind][0][0] = $uid;
                $result->table[$ind][0][1] = $login;
            }

            $user_results = $this->table[$uid];

            foreach ($this->problem_pos as $pid => $pos) {
                if (!$user_results[$pid])
                    $ks = null;
                else {
                    $ks = array();
                    foreach ($this->problem_keys[$pid] as $p_key_ind => $p_key) {
                        $ks[$p_key_ind] = $user_results[$pid][$p_key_ind];
                        if (!isset($ks[$p_key_ind]))
                            $ks[$p_key_ind] = null;
                    }
                }

                $result->table[$ind][$pos + $before_problems_headers] = $ks;
            }
        }

        return $result;
    }

    private function fillTable() {
        $prfx = DB_PREFIX;

        //insert nulls in the table
        foreach ($this->user_login as $uid => $aux1) {
            $this->table[$uid] = array();
            foreach ($this->problem_pos as $pid => $aux2) {
                $this->table[$uid][$pid] = null;
            }
        }

        //request all data
        $request_on_results = sprintf("
            SELECT user_id, problem_id, result
            FROM ${prfx}result
            WHERE contest_id=%s
        ", Data::quote_smart($this->contest_id));
        if ($this->user_id >= 0)
            $request_on_results .= ' AND user_id=' . Data::quote_smart($this->user_id);

        $results_rows = Data::getRows($request_on_results);

        //process each data
        while ($results_row = Data::getNextRow($results_rows)) {
            $uid = $results_row['user_id'];
            $pid = $results_row['problem_id'];
            $result = Data::_unserialize($results_row['result']);

            foreach ($result as $key => $value) {
                $ind = $this->key_to_ind($key, $pid);
                $this->table[$uid][$pid][$ind] = $value;
            }
        }

        //fill forgotten entries in problem keys
        foreach ($this->problem_pos as $pid => $aux)
            if (!$this->problem_keys[$pid])
                $this->problem_keys[$pid] = array('-'); //TODO think of some other text
    }

    /** @var UserDataField[] $users_data */
    private function fill_user_data_keys($users_data) {
        $this->user_data_keys = array();
        if (!$users_data)
            return;

        foreach ($users_data as $df)
            if ($this->is_admin || $df->showInResult)
                $this->user_data_keys[] = $df->data;
    }

    private function consider_problems($contest_id) {
        $prfx = DB_PREFIX;

        //get problem rows, this is needed only to find out the order of problems
        $all_problems_rows = Data::getRows(
            sprintf("SELECT *
                 FROM ${prfx}problem
                 WHERE ${prfx}problem.contest_id=%s
                 ORDER BY ${prfx}problem.contest_pos ASC",
                Data::quote_smart($contest_id))
        );

        //columns with problems
        $this->problem_pos = array();

        while ($problem_row = Data::getNextRow($all_problems_rows)) {
            $id = $problem_row['id'];

            $this->problem_pos[$id] = $problem_row['contest_pos'];
        }
    }

    private function consider_users($contest_id, $user_id, $is_admin) {
        $prfx = DB_PREFIX;
        $query = sprintf("
                SELECT id, login, user_data
                FROM ${prfx}user
                WHERE contest_id=%s",
            Data::quote_smart($contest_id)
        );

        if ($user_id >= 0)
            $query .= ' AND user_id=' . Data::quote_smart($user_id);

        if (!$is_admin)
            $query .= ' AND user_type=' . Data::quote_smart('Participant');

        $this->user_login = array();
        $this->user_data = array();

        $all_users_rows = Data::getRows($query);
        while ($user_row = Data::getNextRow($all_users_rows)) {
            $id = $user_row['id'];

            $this->user_login[$id] = $user_row['login'];
            $this->user_data[$id] = Data::_unserialize($user_row['user_data']);
        }
    }
}